import { ABP } from '@abp/ng.core';
import { Store } from '@ngxs/store';
import { NzMessageService } from 'ng-zorro-antd/message';
import { NzModalService } from 'ng-zorro-antd/modal';
import { EntityListActions } from './entity-list.state.actions';
import { ExtendsEntityListStore } from './entity-list.state.base';
import snq from 'snq';
import { Observable } from 'rxjs';

export abstract class EntityListStateService<T extends { id: string }> {
  public get models(): T[] {
    return this.store.selectSnapshot(this.storeClass['getModels']);
  }
  public get onModelsChanged() {
    return this.store.select(this.storeClass['getModels']);
  }

  public get totalCount(): number {
    return this.store.selectSnapshot(this.storeClass['getTotalCount']);
  }
  public get onTotalCountChanged() {
    return this.store.select(this.storeClass['getTotalCount']);
  }

  public get selectedModels(): T[] {
    return this.store.selectSnapshot(this.storeClass['getSelectedModels']);
  }
  public get onSelectedModelsChanged() {
    return this.store.select(this.storeClass['getSelectedModels']);
  }

  public get isAllSelected(): boolean {
    return this.store.selectSnapshot(this.storeClass['getIsAllSelected']);
  }
  public get onIsAllSelectedChanged() {
    return this.store.select(this.storeClass['getIsAllSelected']);
  }

  public get indeterminate(): boolean {
    return this.store.selectSnapshot(this.storeClass['getIndeterminate']);
  }
  public get onIndeterminateChanged() {
    return this.store.select(this.storeClass['getIndeterminate']);
  }

  public get selectedCount(): number {
    return this.store.selectSnapshot(this.storeClass['getSelectedCount']);
  }
  public get onSelectedCountChanged() {
    return this.store.select(this.storeClass['getSelectedCount']);
  }

  constructor(
    protected storeClass: ExtendsEntityListStore<T>,
    protected store: Store,
    protected modal: NzModalService,
    protected message: NzMessageService
  ) {}

  public new(): Promise<void> {
    return this.navigateToDetail().then(success => {
      if (success) return this.changeToEmpty();
      return Promise.reject();
    });
  }

  public view(id: string): Promise<T> {
    return this.navigateToDetail().then(success => {
      if (success) return this.changeToView(id);
      return Promise.reject();
    });
  }

  public isModelSelected(model: T): boolean {
    return !!snq(() =>
      (this.store.selectSnapshot(this.storeClass['getSelectedModels']) as T[]).find(
        x => x.id == model.id
      )
    );
  }

  targetModelSelect(model: T): Promise<T> {
    return new Promise((resolve, reject) => {
      this.store
        .dispatch(EntityListActions.TargetModelSelect(this.storeClass, model))
        .subscribe(() => resolve(model));
    });
  }

  targetAllSelect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.store
        .dispatch(EntityListActions.TargetAllSelect(this.storeClass))
        .subscribe(() => resolve());
    });
  }

  clearSelect(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.store
        .dispatch(EntityListActions.ClearSelect(this.storeClass))
        .subscribe(() => resolve());
    });
  }

  public delete(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.deleteConfirm().then(() => {
        this.store.dispatch(EntityListActions.Delete(this.storeClass)).subscribe(() => {
          resolve();
        });
      });
    });
  }

  public deleteConfirm(): Promise<void> {
    if (this.selectedCount <= 0) Promise.reject();
    return new Promise((resolve, reject) => {
      this.modal.confirm({
        nzTitle: '提示信息',
        nzContent: `您有已选中<b>${this.selectedCount}</b>条记录，确认要删除所选的记录么？`,
        nzOkText: '确认',
        nzCancelText: '取消',
        nzOnOk: () => resolve(),
      });
    });
  }

  public onQuery(query: ABP.PageQueryParams): Observable<any> {
    return this.store.dispatch(EntityListActions.GetModels(this.storeClass, this.setQuery(query)));
  }

  public setQuery(query) {
    return query;
  }

  public abstract navigateToDetail(): Promise<boolean>;
  public abstract changeToView(id: string): Promise<T>;
  public abstract changeToEmpty(): Promise<void>;
}
